#pragma once

#include <cmath>
#include <iostream>

struct fraction {
	int num;		///< numerator
	unsigned den;	///< denominator

	fraction() {}
	fraction(int n, unsigned d = 1) : num(n), den(d) {}
	operator float() const
		{ return float(num) / den; }
	bool valid() const
		{ return den != 0; }
	bool integer() const
		{ return num % den == 0; }
	//divides numerator and denominator on common divider
	fraction & make_proper();

	friend bool operator == (const fraction & lhs, const fraction & rhs)
		{ return (long long)lhs.num * rhs.den == (long long)lhs.den * rhs.num; }
	friend bool operator < (const fraction & lhs, const fraction & rhs)
		{ return (long long)lhs.num * rhs.den < (long long)lhs.den * rhs.num; }
	friend bool operator <= (const fraction & lhs, const fraction & rhs)
		{ return (long long)lhs.num * rhs.den <= (long long)lhs.den * rhs.num; }
	friend bool operator > (const fraction & lhs, const fraction & rhs)
		{ return (long long)lhs.num * rhs.den > (long long)lhs.den * rhs.num; }
	friend bool operator >= (const fraction & lhs, const fraction & rhs)
		{ return (long long)lhs.num * rhs.den >= (long long)lhs.den * rhs.num; }
	friend bool operator != (const fraction & lhs, const fraction & rhs)
		{ return (long long)lhs.num * rhs.den != (long long)lhs.den * rhs.num; }
};

// inlines

inline fraction & fraction::make_proper()
{
	int n = /*std::*/abs(num);
	int d = den;
	while (n != d) {
		if (n > d)
			n -= d;
		else
			d -= n;
	}
	//n here is greatest common divisor
	num /= n;
	den /= n;
	return * this;
}

//invalid fraction -> empty string
//int fraction ->     num
//others ->           num (num/num)
inline std::ostream & operator << (std::ostream & out, fraction const & frac)
{
	if (!frac.valid())
		return out;
	if (frac.integer())
		return out << frac.num / frac.den;
	std::ios::fmtflags f = out.setf(std::ios::fixed, std::ios::floatfield);
	int pr = out.precision(2);
	out << (float)frac;
	out.precision(pr);
	out.flags(f);
	return out << " (" << frac.num << '/' << frac.den << ')';
}

//supported input formats:
//float
//int/uint
//ignored_num (int/uint)
inline std::istream & operator >> (std::istream & in, fraction & frac)
{
	float f;
	in >> f;
	if (in.fail())
		return in;
	char c;
	in >> c;
	if (in.fail()) {
		in.clear(in.rdstate() & ~std::ios::failbit);
		frac = fraction(int(f * 100 + 0.5f), 100).make_proper();
	}
	else if (c == '/') {
		frac.num = int(f);
		in >> frac.den;
	}
	else if (c == '(') {
		char c1;
		in >> frac.num >> c >> frac.den >> c1;
		if (c != '/' || c1 != ')')
			in.setstate(std::ios::failbit);
	}
	else {
		in.putback(c);
		frac = fraction(int(f * 100 + 0.5f), 100).make_proper();
	}
	return in;
}
